LeetCode #891. 子序列宽度之和
原题
给定一个整数数组 A ,考虑 A 的所有非空子序列。
对于任意序列 S ,设 S 的宽度是 S 的最大元素和最小元素的差。
返回 A 的所有子序列的宽度之和。
示例:
1 | 输入:[2,1,3] |
这是 第98场周赛 上的最后一题,是比较难的。
代码模板:
1 | # 代码 - 0 |
思路
题目要求一个数组的所有子数组的最大最小值的差之和。
对 Python ,有内建的 max() 函数用于求可迭代对象的最大元素。于是问题就只剩下如何获取 list A 的所有子集,得到 A 的所有子集,然后将所有子集的“宽度”相加、return 就行了。
对于求一个 list 的全部子集,有两种途径:
Python 标准库给出了现成的函数供调用:
itertools。
本题要用到combinations()实际上是初始化了一个combinations对象,该对象是一个迭代器,示例如下:1
2
3
4
5
6
7# 代码 - 1
for i in itertools.combinations([2,1,3], 2):
print(i)
# 输出:
# (1, 2)
# (1, 3)
# (2, 3)这个迭代器的元素是
[2,1,3]中两元素组合的所有情形组成的tuple。、用递归或迭代等方法自己实现求得
list子集的函数。
实现(思路1)
首先我们要写一个 width() 函数来求一个整数数组的宽度(题中所述列表中最大最小元素之差):
1 | # 代码 - 2 |
如上,7、8 两行即可实现,很简洁。
随后利用 itertools.combinations 对 A 进行迭代。由于 combinations 是指定元素个数的,我们使用两个 for 循环,第一个循环控制子集的长度,例如 A = [2,1,3] ,则子集长度可能为 1, 2, 3;第二个循环就遍历特定长度的子集,比如 代码 - 1 中写的 for i in itertools.combinations([2,1,3], 2):将遍历 (1, 2)(1, 3)(2, 3) 这三个 tuple。
代码:
1 | # 代码 - 3 |
测试
代码最后面再加几行测试,注意代码不缩进:
1 | # 代码 - 4 |
可见输出结果正确,但是耗时极长,无法通过LeetCode的测试,在 A 较长时此算法没有使用价值。不过想想也知道既然 combinations 是Python内建函数,想必已经在效率问题上做到几乎最好了吧,而967ms这一成绩所受限制是Python的基因。
实现(思路2)
自己实现求子集的函数,尽管效率很可能不如内建函数,但有重大的学习意义。这里仅简要讨论递归方法,若想深入研究或学习其他求子集算法可参考 CSDN博客:求一个集合所有子集的Python实现。
1 | # 代码 - 5 |
测试
1 | # 代码 - 6 |
答案正确,但所耗时长为思路1的两倍。